package com.jiang.operator;

import com.jiang.entity.Block;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

public class BlockUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(BlockUtils.class);

    /**
     * 需要在原有字符串之前加上几个0,str代表0
     * @param str
     * @param repeat
     * @return
     */
    public static String repeat(String str, int repeat){
        //这个更好
        final StringBuilder buf = new StringBuilder();
        for(int i = 0;i < repeat;i++){
            buf.append(str);
        }
        return buf.toString();
    }

    public static boolean isNew(Block block, Block newBlock){
        if(block.getIndex() < newBlock.getIndex()){
            return true;
        }else {
            return false;
        }

    }

    public static boolean isHashValid(String hash, int difficulty){
        //检验该hash是否以几个difficulty0未开始
        String prefix = repeat("0", difficulty);
        return hash.startsWith(prefix);//是否以这几个开始
    }
    /**
     * 计算区块的hash值
     */
    public static String calculateHash(Block block){
        String record = (block.getIndex()) + block.getTimestamp() + (block.getVac()) + block.getPrevHash()
                + block.getMerkleRoot() + block.getNonce();
        MessageDigest digest = DigestUtils.getSha256Digest();
        byte[] hash = digest.digest(StringUtils.getBytesUtf8(record));
        return Hex.encodeHexString(hash);
    }

    /**
     * 区块的生成
     * 将工作量证明机制放到该产生区块之中
     * @param oldBlock
     * @param vac
     * @return
     */
    public static Block generateBlock(Block oldBlock, String[] vac) {
        Block newBlock = new Block();
        newBlock.setIndex(oldBlock.getIndex() + 1);
        newBlock.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        newBlock.setVac(vac);
        newBlock.setMerkleRoot(hashMerkle(Arrays.asList(vac)));
        newBlock.setDifficulty(1);//暂且先设为1
        newBlock.setPrevHash(oldBlock.getHash());
        for(int i = 0;;i++){//这个其实也可以无穷，但是为了序号有用
            String hex = String.format("%x", i);//%x整数类型（十六进制），按照十六进制的格式
            newBlock.setNonce(hex);//随机数无限增加
            if(!isHashValid(calculateHash(newBlock), newBlock.getDifficulty())){
                //查看计算得到的字符串是否是需要的，也就是困难数没有计算成功，需要再次计算
                LOGGER.info("{}need to do more work!", calculateHash(newBlock));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    LOGGER.error("error", e);
                    Thread.currentThread().interrupt();//该线程停止
                }
            }else{
                //BlockChain.blockPower = 1;
                //计算成功之后，将该计算成功的hash设置为新块的hash
                LOGGER.info("{}work done!", calculateHash(newBlock));
                newBlock.setHash(calculateHash(newBlock));
                break;
            }
        }
        newBlock.setHash(calculateHash(newBlock));

        return newBlock;
    }

    /**
     * 校验区块的合法性（有效性）
     *
     * @param newBlock
     * @param oldBlock
     * @return
     */
    public static boolean isBlockValid(Block newBlock, Block oldBlock) {
        if (oldBlock.getIndex() + 1 != newBlock.getIndex()) {
            return false;
        }
        if (!oldBlock.getHash().equals(newBlock.getPrevHash())) {
            return false;
        }
        //来检验得到打包人的nonce
        /**
        if (!calculateHash(newBlock).equals(newBlock.getHash())) {
            return false;
        }*/
        return true;
    }

    /**
     * 如果有别的链比你长，就用比你长的链作为区块链
     *
     * @param oldBlocks
     * @param newBlocks
     * @return 结果链
     */
    public List<Block> replaceChain(List<Block> oldBlocks, List<Block> newBlocks) {
        if (newBlocks.size() > oldBlocks.size()) {
            return newBlocks;
        }else{
            return oldBlocks;
        }
    }

    public static String hashMerkle(List<String> trans){
        //递归
        if(trans.size() == 1){
            return StringUtil.applySha256(trans.get(0));
        }
        List<String> hashTrans = new ArrayList<String>(); //保存每层的交易两两hash
        if(trans.size() % 2==0){
            //交易个数为偶数
            int i = 0;
            while(i < trans.size() - 1){
                String hash = StringUtil.applySha256(trans.get(i)+trans.get(i+1));
                hashTrans.add(hash);//将hash添加到交易中，倒数第二层
                i = i+2;
            }

        }else {//交易数为奇数
            int i = 0;
            while(i < trans.size()-2){
                String hash = StringUtil.applySha256(trans.get(i)+trans.get(i+1));
                hashTrans.add(hash);//将hash添加到交易中，倒数第二层
                i = i+2;
            }
            hashTrans.add(StringUtil.applySha256(trans.get(trans.size()-1)));//单个加密

        }
        return hashMerkle(hashTrans);

    }


}
